﻿using Apewer.Network;
using System;
using System.Collections.Generic;
using System.Text;

namespace Apewer.Web
{

    /// <summary>请求 API 框架的客户端。</summary>
    public class ApiClient
    {

        private const int DefaultTimeout = 60000;

        /// <summary>服务器 URL 根路径。</summary>
        public virtual string UrlRoot { get; set; }

        /// <summary>使用随机值，并验证响应。</summary>
        public virtual bool UseRandom { get; set; }

        /// <summary>账号令牌。</summary>
        public virtual string Ticket { get; set; }

        /// <summary>会话标识。</summary>
        public virtual string Session { get; set; }

        /// <summary>用户代理。</summary>
        public virtual string UserAgent { get; set; }

        /// <summary>请求超时，默认值为 60 秒。</summary>
        public virtual int Timeout { get; set; } = DefaultTimeout;

        void Send(HttpClient client, string random, Action<Json> succeed = null, Action<string> failed = null)
        {
            client.UserAgent = UserAgent;
            var ex = client.Send();
            if (ex != null)
            {
                failed?.Invoke(ex.Message());
                return;
            }

            if (succeed == null && failed == null) return;

            var body = client.ResponseData;
            if (body == null || body.LongLength < 1L)
            {
                failed?.Invoke("服务器返回了空数据。");
                return;
            }

            var json = Json.From(body.Text());
            if (!json)
            {
                failed?.Invoke("服务器返回了非 Json 格式的数据。");
                return;
            }

            var status = json["status"];
            if (status != "ok")
            {
                var message = json["message"];
                failed?.Invoke(message);
                return;
            }

            var data = json.GetProperty("data");
            succeed?.Invoke(data);
        }

        void Send(HttpClient client, string random, Action<byte[]> succeed = null, Action<string> failed = null)
        {
            client.UserAgent = UserAgent;
            var ex = client.Send();
            if (ex != null)
            {
                failed?.Invoke(ex.Message());
                return;
            }

            if (succeed == null) return;

            var body = client.ResponseData;
            succeed?.Invoke(body);
        }

        string Random() => UseRandom ? TextUtility.Random(8) : null;

        #region get

        /// <summary>发起 GET 请求，获取响应中的 data 属性。</summary>
        public void Get(string urlPath, Action<Json> succeed = null, Action<string> failed = null)
        {
            var random = Random();
            var client = Get(urlPath);
            Send(client, random, succeed, failed);
        }

        /// <summary>发起 GET 请求，获取响应的主体。</summary>
        public void Get(string urlPath, Action<byte[]> succeed = null, Action<string> failed = null)
        {
            var random = Random();
            var client = Get(urlPath);
            Send(client, random, succeed, failed);
        }

        HttpClient Get(string urlPath)
        {
            var random = Random();
            var client = new HttpClient();
            client.Url = MergeUrl(urlPath, random);
            client.Method = HttpMethod.GET;
            client.Timeout = Timeout;
            return client;
        }

        /// <summary>发起 POST 请求，上传指定数据，获取响应中的 data 属性。</summary>
        public void Upload(string urlPath, byte[] bytes, Action<Json> succeed = null, Action<string> failed = null)
        {
            var random = Random();
            var client = Upload(urlPath, random, bytes);
            Send(client, random, succeed, failed);
        }

        /// <summary>发起 POST 请求，上传指定数据，获取响应的主体。</summary>
        public void Upload(string urlPath, byte[] bytes, Action<byte[]> succeed = null, Action<string> failed = null)
        {
            var random = Random();
            var client = Upload(urlPath, random, bytes);
            Send(client, random, succeed, failed);
        }

        HttpClient Upload(string urlPath, string random, byte[] bytes)
        {
            var client = new HttpClient();
            client.Url = MergeUrl(urlPath, random);
            client.Method = HttpMethod.POST;
            client.Timeout = Timeout;
            client.RequestContentType = "appliction/octet-stream";
            client.RequestData = bytes;
            return client;
        }

        string MergeUrl(string urlPath, string random)
        {
            var sb = new StringBuilder();
            sb.Append(TextUtility.AssureEnds(UrlRoot, "/", true));

            var query = null as string;
            if (!string.IsNullOrEmpty(urlPath))
            {
                var offset = urlPath.IndexOf("#");
                if (offset > -1) urlPath = urlPath.Substring(0, offset);

                offset = urlPath.IndexOf("?");
                if (offset < 0)
                {
                    sb.Append(urlPath);
                }
                else
                {
                    sb.Append(urlPath.Substring(0, offset));
                    query = urlPath.Substring(offset + 1);
                }
            }

            var session = Session;
            var ticket = Ticket;
            var ps = ApiUtility.Parameters(query, false);
            if (!string.IsNullOrEmpty(random) && !ps.HasKey("random", true)) ps.Add("random", random);
            if (!string.IsNullOrEmpty(session) && !ps.HasKey("session", true)) ps.Add("session", session);
            if (!string.IsNullOrEmpty(ticket) && !ps.HasKey("ticket", true)) ps.Add("ticket", ticket);
            if (ps.Count > 0)
            {
                var first = true;
                foreach (var p in ps)
                {
                    if (first)
                    {
                        sb.Append("?");
                        first = false;
                    }
                    else sb.Append("&");
                    sb.Append(p.Key);
                    sb.Append("=");
                    sb.Append(p.Value);
                }
            }
            var url = sb.ToString();
            return url;
        }

        #endregion

        #region post

        /// <summary>发起 POST 请求，获取响应中的 data 属性。</summary>
        public void Post(string urlPath, Json data, Action<Json> succeed = null, Action<string> failed = null)
        {
            var random = Random();
            var client = Post(urlPath, random, null, null, data);
            Send(client, random, succeed, failed);
        }

        /// <summary>发起 POST 请求，获取响应中的 data 属性。</summary>
        public void Post(string application, string function, Json data, Action<Json> succeed = null, Action<string> failed = null)
        {
            var random = Random();
            var client = Post(null, random, application, function, data);
            Send(client, random, succeed, failed);
        }

        /// <summary>发起 POST 请求，获取响应的主体。</summary>
        public void Post(string urlPath, Json data, Action<byte[]> succeed = null, Action<string> failed = null)
        {
            var random = Random();
            var client = Post(urlPath, random, null, null, data);
            Send(client, random, succeed, failed);
        }

        /// <summary>发起 POST 请求，获取响应的主体。</summary>
        public void Post(string application, string function, Json data, Action<byte[]> succeed = null, Action<string> failed = null)
        {
            var random = Random();
            var client = Post(null, random, application, function, data);
            Send(client, random, succeed, failed);
        }

        HttpClient Post(string urlPath, string random, string application, string function, Json data)
        {
            var json = Json.NewObject();
            if (!string.IsNullOrEmpty(application)) json.SetProperty("application", application);
            if (!string.IsNullOrEmpty(function)) json.SetProperty("function", function);
            if (!string.IsNullOrEmpty(random)) json.SetProperty("random", random);

            var session = Session;
            if (!string.IsNullOrEmpty(session)) json.SetProperty("session", session);

            var ticket = Ticket;
            if (!string.IsNullOrEmpty(random)) json.SetProperty("ticket", ticket);

            if (data != null) json.SetProperty("data", data);

            var url = TextUtility.AssureEnds(UrlRoot, "/", true);
            if (!string.IsNullOrEmpty(urlPath)) url += urlPath.StartsWith("/") ? urlPath.Substring(1) : urlPath;

            var client = new HttpClient();
            client.Url = url;
            client.Method = HttpMethod.POST;
            client.Timeout = Timeout;
            client.RequestContentType = "application/json";
            client.RequestData = json.ToString().Bytes();
            return client;
        }

        #endregion

    }

}
